package com.wkm.chainchain.blockchain.util;

import com.wkm.chainchain.blockchain.block.Block;
import com.wkm.chainchain.blockchain.block.BlockBody;
import com.wkm.chainchain.blockchain.block.BlockHeader;
import com.wkm.chainchain.blockchain.block.Unit;
import org.apache.commons.codec.binary.Hex;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import static com.wkm.chainchain.blockchain.BlockChainOp.queryBlock;
import static com.wkm.chainchain.blockchain.BlockChainOp.queryHeight;
import static com.wkm.chainchain.blockchain.constant.ConstVariable.GENESIS_HASH_PREV_BLOCK;
import static com.wkm.chainchain.blockchain.util.HashUtil.HASH;

public class BlockUtil {

    /**
     * 构造区块的函数，通过区块体中的unitList来计算区块头，最终形成一个区块
     *
     * @param unitList 区块体中的所有unit组成的list
     * @return 返回一个区块
     */
    public static Block constructBlock(ArrayList<Unit> unitList) {
        BlockBody blockBody = new BlockBody(unitList);
        BlockHeader blockHeader = new BlockHeader();
        // hashPrevBlock、hashMerkleRoot、timestamp、height
        int curHeight = queryHeight();
        blockHeader.setHeight(curHeight + 1);
        blockHeader.setTimestamp(String.valueOf(System.currentTimeMillis()));
        blockHeader.setHashMerkleRoot(getHashMerkleRoot(unitList));

        if (curHeight == 0) {
            // 说明当前所要创建的区块是创世区块
            blockHeader.setHashPrevBlock(GENESIS_HASH_PREV_BLOCK);
        } else {
            // 当前所要创建的区块不是创世区块，那么hashPrevBlock就是上一个区块的哈希值
            Block curBlock = queryBlock(curHeight + "");
            blockHeader.setHashPrevBlock(curBlock.getHeader().getBlockHash());
        }

        return new Block(blockHeader, blockBody);
    }

    /**
     * 通过ArrayList，计算区块体中的所有Unit对应的区块头中的hashMerkleRoot
     *
     * @param unitList 区块体中的所有Unit组成的list
     * @return 返回一个16进制的hashMerkleRoot
     */
    // TODO 错误的merkle tree，有待修正
    public static String getHashMerkleRoot(ArrayList<Unit> unitList) {
        // 获取MerkleRoot过程中需要用到的ArrayList
        List<byte[]> processList = unitList.stream().map(item -> HASH.digest(item.toString().getBytes())).collect(Collectors.toList());
        ArrayList<byte[]> tempList = null;
        do {
            tempList = new ArrayList<>();
            for (int i = 0; i < processList.size(); i += 2) {
                byte[] digest1 = HASH.digest(processList.get(i));
                if (processList.size() > (i + 1)) {
                    // <= i+1，换言之processList.get(i+1)为null
                    byte[] digest2 = HASH.digest(processList.get(i + 1));
                    // 合并两个字节数组
                    byte[] digest = new byte[digest1.length + digest2.length];
                    // Object src, int srcPos, Object dest, int destPos, int length
                    System.arraycopy(digest1, 0, digest, 0, digest1.length);
                    System.arraycopy(digest2, 0, digest, digest1.length, digest2.length);
                    // 添加到list
                    tempList.add(digest);
                } else {
                    tempList.add(digest1);
                }
                processList = tempList;
            }
        } while (tempList.size() != 1);
        char[] hexRes = Hex.encodeHex(tempList.get(0));
        return new String(hexRes);
    }
}
